#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>

using namespace std;

//在进入下面的的学习之前,需要了解一些信号的其他概念
//1.事件执行信号的处理动作称为信号递达
//2.信号从产生到抵达之间的状态叫做信号未决
//3.进程可以选择阻塞某个信号
//4.被阻塞的信号产生时将保持未决状态,知道进程解除次信号的阻塞,才执行抵达的动作
//5.注意阻塞和忽略时不同的,只要信号被阻塞久不回抵达,而忽略是抵达之后的可选的一种处理动作

//如何理解OS给进程发送信号?
//其实就是OS发送信号数据给进程的task_struct,本质就是向指定的进程的task_struct中的信号位图(pending位图)写入比特位,即完成了信号的发送
//信号的发送其实就是信号的写入

//其实我们细心就会发现信号的取值范围是[1,31]
//那么我们可以联想到就是32位的数据类型
//实际上进程的PCB中保存了进程的各种属性
//进程PCB一定要有对应的数据变量,用来记录是否收到了对应的信号
//如果收到了对应的信号,比特位的第几个位置就由0变为1

//信号在进程的PCB中(内核)中由三个部分组成
//一个是block(阻塞)位图,一个是pending(未决)位图,还有一个函数指针数组,handler表,每个信号的编号,就是该数组的下标
//前两个位图的第几个比特位就代表的是第几个信号,第三个也可以根据数组下标来寻找信号
//block位图0代表这个比特位信号没有阻塞,如果是1就代表这个信号阻塞
//pending位图0代表没有接收到这个比特位信号,如果是1就代表接受到这个信号
//而handler表有默认的函数指针,代表默认的信号处理方式

//假如SIGQUIT信号未产生过,它的block位图位置是1,说明这个信号被阻塞了
//而对这个信号进行过捕捉,执行用户的自定义函数
//如果在进程接触对某信号的阻塞之前这个信号产生了多次,该如何进行处理?
//允许系统传递该信号一次或者多次,如果是常规信号,在信号递达之前产生多次,但是只记一次(pending位图只有0和1的状态)
//而实时信号在信号传达之前产生多次可以依次放在一个队列里面(我们不做讨论)

//上面说过每个信号只有一个bit位的标志,非0即1,不记录这个记录产生了多少次,阻塞标志也是这样表示
//所以pending位图和block位图可以用同一个数据类型来进行储存,sigset_t
//sigset_t称为信号集,里面封装了位图的结构,这个类型可以表示每个信号的"无效"和"有效"的状态
//在阻塞信号集中"有效"和"无效"的含义是该信号是否被阻塞,而在未决信号集中"有效"和"无效"的含义就是这个信号是否处于未决状态
//阻塞信号集也叫做当前进程信号屏蔽字(signal_mask),这里的"屏蔽"应该理解为阻塞而不是忽略

//sigset_t类型对于每种信号用一个bit表示“有效”或“无效”状态,至于这个类型内部如何存储这些bit则依赖于系统
//实现,从使用者的角度是不必关心的,使用者只能调用以下函数来操作sigset_ t变量,而不应该对它的内部数据做
//任何解释,比如用printf直接打印sigset_t变量是没有意义的

//信号集操作的一些函数
//sigemptyset
//功能:
//    初始化set指向的信号集,使其中所有信号对应的bit位清零,表示该信号集不包含任何有效信号
//原型:
//    int sigemptyset(sigset_t* set);
//返回值:
//    成功0,失败-1

//sigfillset
//功能:
//    初始化set指向的信号集,是其中所有信号bit位置为1,并且屏蔽所有信号
//原型:
//    int sigfillset(sigset_t* set);
//返回值:
//    成功0,失败-1

//sigaddset
//功能:
//    在该信号集中添加某种有效信号
//原型:
//    int sigaddset(sigset* set, int signo)
//返回值:
//   成功返回0, 失败返回-1

//sigdelset
//功能:
//    在该信号集中删除某种有效信号
//原型:
//    int sigdelset(sigset* set, int signo)
//返回值:
//   成功返回0, 失败返回-1

//sigismember
//功能:
//    用于判断一个信号集的有效信号中是否包含某种信号
//原型:
//    int sigismember(const sigset_t* set, int signo);
//返回值:
//    如果包好某个信号就返回1,不包含就返回0,出错就返回-1

//sigprocmask
//功能:
//    读取或者修改进程的信号屏蔽字(阻塞信号集)
//原型:
//    int sigprocmask(int how, const sigset_t* set, sigset_t* oset);
//参数:
//    如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出
//    如果set是非空指针,则更改进程的信号屏蔽字,参数how提示如何更改
//    如果oset和set都是非空指针,则将原来的信号屏蔽字备份到oset里,然后
//根据set和how参数更改信号屏蔽字,假设当前的信号屏蔽字为mask,下面说明了how参数的可选值
//    SIG_BLOCK:set包含了我们希望添加到当前信号屏蔽字的信号,相当于mask=mask | set 
//    SIG_UNBLOCK:set包含了我们希望从当前信号屏蔽字中解除阻塞的信号,相当于mask=mask & ~set 
//    SIG_SETMASK:设置当前信号屏蔽字set所指向的值,相当于mask=set 
//返回值:
//   成功返回0,出错返回-1
//ps:
//   如果调用sigpeocmask解除了对当前若干个未决信号的阻塞,则在sigprocmask返回前,至少将其中一个信号递达

//sigpending
//功能:
//    读取当前进程的未决信号集,通过set参数传出
//原型:
//   int sigpending(sigset_t* set);
//返回值:
//   成功返回0,失败返回-1

//通过设置阻塞信号集,过去未决信号集

//void handler(int signo)
//{
//    cout << signo << "号信号成功递达!" << endl;
//}
//
//void show_pending(const sigset_t* set)
//{
//    //模拟打印pending位图的结构
//    cout << "current process pending:";
//    for (int i = 1; i <= 31; ++i)
//    {
//        if (sigismember(set, i))
//        {
//            cout << "1";
//        }
//        else 
//        {
//            cout << "0";
//        }
//    }
//    cout << endl;
//}
//
//int main()
//{
//    //捕捉2,3,4信号
//    signal(2, handler);
//    signal(4, handler);
//    signal(3, handler);
//
//    //虽然sigset_t是一个位图的结构,但是不同的OS的封装是不一样的,所以不能让用户直接修改
//    //set是一个变量,这个变量也是保存在用户栈上面的
//    sigset_t iset;
//    sigset_t oset;
//
//    //清空信号集
//    sigemptyset(&iset);
//    sigemptyset(&oset);
//
//    //向信号集里面添加指定信号
//    sigaddset(&iset, 2);
//    sigaddset(&iset, 4);
//    sigaddset(&iset, 3);
//
//    //设置当前进程的屏蔽字
//    sigprocmask(SIG_BLOCK, &iset, &oset);
//
//    int count = 0;
//    sigset_t pending;
//    while(1)
//    {
//        sigemptyset(&pending);
//
//        //获取pending位图
//        sigpending(&pending);
//
//        show_pending(&pending);
//
//        count++;
//
//        sleep(1);
//
//        if (count == 30)
//        {
//            //恢复进程的屏蔽字
//            sigprocmask(SIG_UNBLOCK, &iset, &oset);
//            cout << "恢复2号和3号和4号信号,可以被递达了" << endl;
//        }
//    }
//    return 0;
//}

void handler(int signo)
{
    cout << signo << "号信号成功递达!" << endl;
}

void show_pending(const sigset_t* set)
{
    //模拟打印pending位图的结构
    cout << "current process pending:";
    for (int i = 1; i <= 31; ++i)
    {
        if (sigismember(set, i))
        {
            cout << "1";
        }
        else 
        {
            cout << "0";
        }
    }
    cout << endl;
}

int main()
{
    //捕捉2,3,4信号
    signal(2, handler);
    signal(4, handler);
    signal(3, handler);

    //虽然sigset_t是一个位图的结构,但是不同的OS的封装是不一样的,所以不能让用户直接修改
    //set是一个变量,这个变量也是保存在用户栈上面的
    sigset_t iset;
    sigset_t oset;

    //信号集全部置为1
    sigfillset(&iset);
    //清空信号集
    sigemptyset(&oset);

    //设置当前进程的屏蔽字
    sigprocmask(SIG_SETMASK, &iset, &oset);

    int count = 0;
    sigset_t pending;
    while(1)
    {
        sigemptyset(&pending);

        //获取pending位图
        sigpending(&pending);

        show_pending(&pending);

        count++;

        sleep(1);

        if (count == 30)
        {
            //恢复进程的屏蔽字
            sigprocmask(SIG_SETMASK, &oset, nullptr);
            cout << "恢复2号和3号和4号信号,可以被递达了" << endl;
        }
    }
    return 0;
}

